Skip to content

Conversation

@Fredi-raspall
Copy link
Contributor

@Fredi-raspall Fredi-raspall commented Jan 13, 2026

Addresses #1174

  • Adds a simple validator, as a rust binary.
  • The intent is to compile this validator as wasm/wasi and use it from fabricator.
  • The validator expects a GatewayAgent CRD from stdin and produces a JSON-encoded response in stdout.
  • The output JSON describes if the CRD could be validated or otherwise the problem encountered.
  • The "errors" include issues that the user may not be responsible for, but which prevent the validation from happening.
  • The errors are organized mimicking the sequence of steps done by dataplane when validating a config. Roughly:
    • io (specific to this tool)
    • deserialize from json (we should not see these in prod, but if CRD is manually created)
    • metadata issues (these should be integration bugs)
    • conversion to config (e.g. bad values vni = 0 or > 2^24 )
    • config errors (these are the ones the user is responsible for).
      • these type of errors contain as a list/sequence
      • however, currently we only return one element because dataplane validation early exits when the first issue is encountered.

I will open a separate PR to:
* refine the feedback and the information provided by some of the errors.
* move some checks from conversion to validation, as they pertain there.
* see if an exhaustive (non-early-exited) validation can be added.

Sample deserialization errors
In these errors, a list of lines (context) near the issue is returned. The line number is not too exact (depending on the error type it is offset by +/-1 or 2). We should seldom (if ever) see deserialization errors when the input JSON is machine generated. So this is mostly for testing manually crafted configs.

{
  "DeserializeError": {
    "hint": "trailing comma at line 8 column 3",
    "line": 8,
    "column": 3,
    "category": "Syntax",
    "context": {
      "3": "  \"metadata\": {",
      "4": "    \"generation\": 1,",
      "5": "    \"name\": \"gateway-1\",",
      "6": "    \"namespace\": \"fab\",",
      "7": "  },",
      "8": "  \"spec\": {",
      "9": "  \"agentVersion\": \"v0.32.0\",",
      "10": "  \"communities\": {",
      "11": "    \"0\": \"65000:800\",",
      "12": "    \"1\": \"65000:801\","
    }
  }
}
{
  "DeserializeError": {
    "hint": "invalid number at line 150 column 15",
    "line": 150,
    "column": 15,
    "category": "Syntax",
    "context": {
      "145": "    },",
      "146": "    \"VPC-2\": {",
      "147": "      \"internalID\": \"BBBBB\",",
      "148": "      \"subnets\": null,",
      "149": "      \"vni\": 000",
      "150": "    },",
      "151": "    \"VPC-3\": {",
      "152": "      \"internalID\": \"CCCCC\",",
      "153": "      \"subnets\": null,",
      "154": "      \"vni\": 2000"
    }
  }
}
{
  "DeserializeError": {
    "hint": "invalid type: string \"65000-BAD\", expected u32 at line 20 column 22",
    "line": 20,
    "column": 22,
    "category": "Data",
    "context": {
      "15": "    \"4\": \"65000:804\",",
      "16": "    \"5\": \"65000:805\"",
      "17": "  },",
      "18": "  \"gateway\": {",
      "19": "    \"asn\": \"65000-BAD\",",
      "20": "    \"groups\": [",
      "21": "      {",
      "22": "        \"name\": \"gw-group-1\",",
      "23": "        \"priority\": 10",
      "24": "      },"
    }
  }
}

{
  "DeserializeError": {
    "hint": "invalid value: integer `-1500`, expected u32 at line 79 column 20",
    "line": 79,
    "column": 20,
    "category": "Data",
    "context": {
      "74": "    },",
      "75": "    \"protocolIP\": \"7.0.0.100/32\",",
      "76": "    \"vtepIP\": \"7.0.0.100/32\",",
      "77": "    \"vtepMAC\": \"02:aa:bb:cc:dd:ee\",",
      "78": "    \"vtepMTU\": -1500,",
      "79": "    \"workers\": 1",
      "80": "  },",
      "81": "  \"groups\": {",
      "82": "    \"gw-group-1\": {",
      "83": "      \"members\": ["
    }
  }
}

Sample Metadata errors

{
  "MetadataError": "Missing gateway name"
}
{
  "MetadataError": "Missing generation Id"
}

Sample Conversion errors

{
  "ConversionError": "Invalid Gateway Agent object: Could not create VPC: Bad VPC Id"
}
{
  "ConversionError": "Could not parse value: Invalid CIDR format: 192.168.90.0/34: Invalid Prefix: 192.168.90.0/34"
}
{
  "ConversionError": "Invalid Gateway Agent object: Could not create VPC: '0' is not a valid VNI"
}

Sample Configuration errors

{
  "Configuration": {
    "errors": [
      "A VPC peering object refers to non-existent VPC 'VPC-1'"
    ]
  }
}

Success case

"Success"

@Fredi-raspall Fredi-raspall requested a review from a team as a code owner January 13, 2026 09:59
@Fredi-raspall Fredi-raspall requested review from Frostman and daniel-noland and removed request for a team January 13, 2026 09:59
@Fredi-raspall Fredi-raspall self-assigned this Jan 13, 2026
Copy link
Member

@Frostman Frostman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Fredi-raspall, we need to agree on the output format; the current one would be very difficult to consume to produce a user-friendly message and difficult to parse on top of it. I'd suggest to at least flatten it a bit into list of errors and move the error type into field type instead of it being a key and always have an object as a root, e.g.:

{
  "success": true
}

or

{
  "errors": [
{
   "type": "DeserializeError",
    "message": "invalid value: integer `-1500`, expected u32 at line 79 column 20",
    "line": 79,
    "column": 20,
    "category": "Data",
    "context": {
      "74": "    },",
      "75": "    \"protocolIP\": \"7.0.0.100/32\",",
      "76": "    \"vtepIP\": \"7.0.0.100/32\",",
      "77": "    \"vtepMAC\": \"02:aa:bb:cc:dd:ee\",",
      "78": "    \"vtepMTU\": -1500,",
      "79": "    \"workers\": 1",
      "80": "  },",
      "81": "  \"groups\": {",
      "82": "    \"gw-group-1\": {",
      "83": "      \"members\": ["
    }
},
{
  "type": "ConversionError",
  "message": "Invalid Gateway Agent object: Could not create VPC: '0' is not a valid VNI"
},
{
  "type": "ConfigurationError",
  "message": "blah blah 1"
},
{
  "type": "ConfigurationError",
  "message": "blah blah 2"
},
{
  "type": "ConfigurationError",
  "message": "blah blah 3"
},
  ]
}

I additionally suggest renaming hint to message and making it mandatory for all errors to have a message.

ordermap = { workspace = true, features = ["serde"] }

serde = { workspace = true }
serde_json = { workspace = true } No newline at end of file
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

newline missing

@Fredi-raspall
Copy link
Contributor Author

@Fredi-raspall, we need to agree on the output format; the current one would be very difficult to consume to produce a user-friendly message and difficult to parse on top of it. I'd suggest to at least flatten it a bit into list of errors and move the error type into field type instead of it being a key and always have an object as a root, e.g.:

Ok, I'll see what I can do. Note that some of the errors are exclusive. I.e., if we fail to deserialize, we'll not be able to validate the config. So you'll never see "DeserializeError" along with "ConversionError".

I additionally suggest renaming hint to message and making it mandatory for all errors to have a message.

Ok!

@Fredi-raspall Fredi-raspall force-pushed the pr/fredi/minimal_validator branch from 5ec6a34 to b4cdc48 Compare January 14, 2026 09:18
@Fredi-raspall
Copy link
Contributor Author

@Frostman , I've pushed a new version with a simplified output.

Samples

{
  "success": true,
  "errors": []
}
{
  "success": false,
  "errors": [
    {
      "type": "Metadata",
      "message": "Invalid generation Id"
    }
  ]
}
{
  "success": false,
  "errors": [
    {
      "type": "Deserialization",
      "subtype": "Syntax",
      "message": "trailing comma at line 8 column 3"
    }
  ]
}
{
  "success": false,
  "errors": [
    {
      "type": "Deserialization",
      "subtype": "Data",
      "message": "invalid value: integer `-2000`, expected u32 at line 155 column 18"
    }
  ]
}
{
  "success": false,
  "errors": [
    {
      "type": "Configuration",
      "message": "A VPC peering object refers to non-existent VPC 'VPC-1'"
    }
  ]
}

I wonder if we should add an optional "rule" field (or "rule-violation") that explains the user the reason why a configuration may be rejected in general terms. E.g.

{
  "success": false,
  "errors": [
    {
      "type": "Configuration",
      "message": "The peering configuration of VPC VPC-1 is not correct:  peering X is bad"
      "rule": "A VPC can only peer with ... blah, blah. 
      Suggestion: either you do X or you do Y. Both are not currently supported."
    }
  ]
}

@Fredi-raspall Fredi-raspall added the ci:-upgrade Disable VLAB upgrade tests label Jan 14, 2026
@Fredi-raspall Fredi-raspall force-pushed the pr/fredi/minimal_validator branch 3 times, most recently from 5fd28e6 to 799b546 Compare January 15, 2026 21:23
@Frostman
Copy link
Member

Tried the latest binary @Fredi-raspall shared in Slack, and it looks ok. I think we can merge in that state and main work would be to make all validation messages as user friendly as possible.

@Fredi-raspall
Copy link
Contributor Author

Tried the latest binary @Fredi-raspall shared in Slack, and it looks ok. I think we can merge in that state and main work would be to make all validation messages as user friendly as possible.

I will squash the 3 commits first.

@Fredi-raspall Fredi-raspall force-pushed the pr/fredi/minimal_validator branch from 8c4d0fa to f0ec3a1 Compare January 16, 2026 09:14
@Fredi-raspall Fredi-raspall added the ci:-vlab Disable VLAB tests label Jan 16, 2026
We need a new crate to be able to validate gw configurations offline,
without starting dataplane. This patch adds a vanilla validator,
as a rust binary. The intent is to compile this validator as wasm/wasi
and use it externally. The validator expects a GatewayAgent CRD from
stdin in JSON/YAML and produces a YAML-encoded response in stdout.
If the response cannot be serialized, an error is written to stderr.

On success, the validator outputs:

success: true
errors: []

In case the configuration is invalid, or cannot be validated for
some reason (e.g. malformed json/yaml), a yaml-encoded string is
output to stderr, with success = false and the errors populated.

E.g.

success: false
errors:
- type: Conversion
  message: 'Invalid Gateway Agent object: Could not create VPC: ''0''
           is not a valid VNI'

or

success: false
errors:
- type: Deserialization
  message: 'spec.gateway.neighbors[1].asn: invalid value:
           integer `6500200000000`, expected u32 at line 68 column 16'

Each error includes a type and a text message describing the problem.
The possible error types mirror the sequence of steps the validator
takes to evaluate a configuration and is one of:

 "Environment": for errors of the tool
 "Deserialization": for malformed inputs or invalid types
 "Metadata": for illegal or missing metadata fields
 "Conversion": for errors converting CRD to configuration
 "Configuration": for missing or semantically incorrect configs

In general, only the last two types of errors may be due to an
incorrect configuration from the user. E.g.

success: false
errors:
- type: Configuration
  message: A VPC peering object refers to non-existent VPC 'VPC-1'

Signed-off-by: Fredi Raspall <[email protected]>
@Fredi-raspall Fredi-raspall force-pushed the pr/fredi/minimal_validator branch from f0ec3a1 to 1186057 Compare January 16, 2026 09:42
@Fredi-raspall Fredi-raspall added this pull request to the merge queue Jan 16, 2026
Merged via the queue into main with commit 40b898d Jan 16, 2026
22 checks passed
@Fredi-raspall Fredi-raspall deleted the pr/fredi/minimal_validator branch January 16, 2026 11:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:-upgrade Disable VLAB upgrade tests ci:-vlab Disable VLAB tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants